/**
 * Define la clase App que se encarga de crear la ventana, inicializar la
 * aplicación y manejar el bucle del juego cargando el resto del contenido
 *
 * @file app.cpp
 * @author Adrián Guerra Marrero
 * @date 20110419
 */

#include "App.h"

namespace SGE
{

App::App(const std::string theTitle) :
	mTitle(theTitle),
	mVideoMode(DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT, DEFAULT_VIDEO_BPP),
	mWindow(),
	mWindowSettings(),
	mWindowStyle(sf::Style::Close | sf::Style::Resize),
	mInput(mWindow.GetInput()),
	mExitCode(0),
	mRunning(false),
	mUpdateRate(1.0f / 100)
{
	// Creamos el archivo log
	mLogFile.assign("log.txt");
	mLog.open(mLogFile.c_str());
	mLog << "LogFile: " << mLogFile << std::endl;

	// Escribimos en el log
	mLog << "App::App() ctor called" << std::endl;
}

App::~App()
{
	mRunning = false;
	// Output to log file
	mLog << "App::~App() dtor called" << std::endl;
}

void App::ProcessArguments(int argc, char* argv[])
{
	// Manejador de los comandos pasados por consola
	// TODO: Añadir manejador de comandos
	mLog << "App::ProcessArguments() Program: " << argv[0] << std::endl;
	mLog << "App::ProcessArguments() Command Line: ";
	for(int iloop = 1; iloop<argc; iloop++)
	{
		mLog << argv[iloop] << ", ";
	}
	mLog << std::endl;
}

int App::Run(void)
{
	// Log inicio del método Run
	mLog << "App::Run() starting" << std::endl;

	// Cambiamos a true la flag de ejecución
	mRunning = true;

	// Pre-init is responsible for the following:
	// 1) Opening our configuration file
	// 2) Setting up our render window
	PreInit();

	// Initialize our application which might set our Running flag to false
	Init();

	// Loop if Running flag is still true
	Loop();

	// Cleanup our application
	Cleanup();

	// Make sure our Running flag is set to false before exiting
	mRunning = false;

	// Log our Exit Code value
	mLog << "App::Run() returning with " << mExitCode << std::endl;

	// Return the Exit Code specified by Quit or 0 of Quit was never called
	return mExitCode;
}

bool App::IsRunning(void) const
{
	return mRunning;
}

float App::GetUpdateRate(void) const
{
	return (1.0f / mUpdateRate);
}

void App::SetUpdateRate(float theRate)
{
	if(1000.0f >= theRate && 1.0f <= theRate)
	{
		mUpdateRate = 1.0f / theRate;
	}
}

void App::Quit(int theExitCode)
{
	mExitCode = theExitCode;
	mRunning = false;
}

void App::PreInit(void)
{
	// What size window does the user want?
	mVideoMode.Width = DEFAULT_VIDEO_WIDTH;
	mVideoMode.Height = DEFAULT_VIDEO_HEIGHT;
	mVideoMode.BitsPerPixel = DEFAULT_VIDEO_BPP;

	// For Fullscreen, verify valid VideoMode, otherwise revert to defaults for Fullscreen
	if(sf::Style::Fullscreen == mWindowStyle && false == mVideoMode.IsValid())
	{
		mVideoMode.Width = DEFAULT_VIDEO_WIDTH;
		mVideoMode.Height = DEFAULT_VIDEO_HEIGHT;
		mVideoMode.BitsPerPixel = DEFAULT_VIDEO_BPP;
	}

	// Create a RenderWindow object using VideoMode object above
	mWindow.Create(mVideoMode, mTitle, mWindowStyle, mWindowSettings);

	// Use Vertical Sync
	mWindow.UseVerticalSync(true);

	// Output to log file
	mLog << "App::PreInit() completed" << std::endl;
}

void App::Init(void)
{
	// Output to log file
	mLog << "App::Init() completed" << std::endl;
}

void App::Loop(void)
{
	// Clock used in restricting Update loop to a fixed rate
	sf::Clock anUpdateClock;
	anUpdateClock.Reset();

	// When do we need to update next?
	float anUpdateNext = anUpdateClock.GetElapsedTime();

	// Loop while IsRunning returns true
	while(IsRunning() && mWindow.IsOpened())
	{
		// Create a fixed rate Update loop
		while(anUpdateClock.GetElapsedTime() > anUpdateNext)
		{
			// Handle some events and let the current active state handle the rest
			sf::Event anEvent;
			while(mWindow.GetEvent(anEvent))
			{
				// Switch on Event Type
				switch(anEvent.Type)
				{
				case sf::Event::Closed:       // Window closed
					Quit(StatusAppOK);
					break;
				case sf::Event::GainedFocus:  // Window gained focus
					break;
				case sf::Event::LostFocus:    // Window lost focus
					break;
				case sf::Event::Resized:      // Window resized
					break;
				} // switch(anEvent.Type)
			} // while(mWindow.GetEvent(anEvent))


			// Update our update next time
			anUpdateNext += mUpdateRate;
		} // while(anUpdateClock.GetElapsedTime() > anUpdateNext)

		// Display Render window to the screen
		mWindow.Clear(sf::Color(150, 150, 150));
		mWindow.Display();

	} // while(IsRunning() && !mStates.empty())
}

void App::Cleanup(void)
{

	// Close the Render window if it is still open
	if(mWindow.IsOpened())
	{
		// Show the Mouse cursor
		mWindow.ShowMouseCursor(true);

		// Close the Render window
		mWindow.Close();
	}

	// Output to log file
	mLog << "App::Cleanup() completed" << std::endl;
}

} // Namespace SGE
